/**
 * LangChain AI指令解析器
 * 使用LangChain和OpenAI API解析自然语言指令
 */

// 更新导入路径
import { ChatOpenAI } from '@langchain/openai';
import { 
  PromptTemplate,
  ChatPromptTemplate,
  SystemMessagePromptTemplate,
  HumanMessagePromptTemplate 
} from '@langchain/core/prompts';
import { 
  StructuredOutputParser
} from '@langchain/core/output_parsers';
import { TestAction } from '../types';
import { createContextLogger } from '../utils/logger';
import dotenv from 'dotenv';

// 加载环境变量
dotenv.config();

// 创建日志记录器
const logger = createContextLogger('LangChainParser');

// 确保API密钥已设置
if (!process.env.DEEPSEEK_API_KEY) {
  logger.error('未设置DEEPSEEK_API_KEY环境变量');
}

// 创建LLM实例
const llm = new ChatOpenAI({
  modelName: 'deepseek-chat', // 使用 Deepseek 模型
  temperature: 0.2,
  openAIApiKey: process.env.DEEPSEEK_API_KEY,
  configuration: {
    baseURL: process.env.DEEPSEEK_API_BASE || 'https://api.deepseek.com/v1',
  }
});

// 修改输出解析器的定义
const outputParser = StructuredOutputParser.fromNamesAndDescriptions({
  actions: "测试动作数组，每个动作包含type、selector、value等属性",
});

// 修改提示模板
const systemTemplate = SystemMessagePromptTemplate.fromTemplate(
  '你是一个测试自动化专家，负责生成测试动作序列。请确保返回的是有效的JSON格式。'
);

const humanTemplate = HumanMessagePromptTemplate.fromTemplate(`
请根据以下测试描述，生成一系列测试动作：

{prompt}

测试应该在网站 {baseUrl} 上执行。

可用的动作类型包括：
- navigate: 导航到URL
- click: 点击元素
- fill: 填充表单字段
- select: 选择下拉选项
- hover: 悬停在元素上
- press: 按键
- wait: 等待
- assert: 断言
- screenshot: 截图

请确保为每个动作提供适当的选择器（CSS选择器或XPath）和值。
`);

const promptTemplate = ChatPromptTemplate.fromMessages([
  systemTemplate,
  humanTemplate
]);

// 在 generateActionsFromPrompt 函数中修改解析响应部分
export async function generateActionsFromPrompt(prompt: string, baseUrl?: string): Promise<TestAction[]> {
  try {
    logger.info(`解析测试描述: ${prompt}`);
    
    // 创建消息
    const messages = await promptTemplate.formatMessages({
      prompt,
      baseUrl: baseUrl || 'http://localhost:3000'
    });
    
    // 调用 LLM
    const response = await llm.invoke(messages);

    // 解析响应
    try {
      // 处理 response.content 可能是复杂类型的情况
      const content = typeof response.content === 'string' 
        ? response.content 
        : JSON.stringify(response.content);
      
      // 尝试直接解析JSON
      const jsonMatch = content.match(/```json\n([\s\S]*?)\n```/) || 
                        content.match(/```\n([\s\S]*?)\n```/) ||
                        [null, content];
      
      const jsonContent = jsonMatch[1]?.trim() || content.trim();
      
      try {
        // 尝试解析为JSON
        const parsedJson = JSON.parse(jsonContent);
        
        // 检查是否为数组
        if (Array.isArray(parsedJson)) {
          logger.info('直接解析为动作数组');
          return parsedJson.map(item => ({
            type: item.action || item.type,
            selector: item.selector,
            value: item.value || item.url,
            timeout: item.timeout
          }));
        } 
        // 检查是否有actions字段
        else if (parsedJson.actions && Array.isArray(parsedJson.actions)) {
          logger.info('解析为包含actions字段的对象');
          return parsedJson.actions;
        }
        // 检查是否有test_actions字段
        else if (parsedJson.test_actions && Array.isArray(parsedJson.test_actions)) {
          logger.info('解析为包含test_actions字段的对象');
          return parsedJson.test_actions.map((item: any) => ({
            type: item.action || item.type,
            selector: item.selector,
            value: item.value || item.url,
            timeout: item.timeout
          }));
        }
      } catch (jsonError) {
        logger.error('JSON解析失败，尝试使用结构化解析器', { error: jsonError });
      }
      
      // 回退到结构化解析器
      const parsedResponse = await outputParser.parse(content);
      if (Array.isArray(parsedResponse.actions)) {
        return parsedResponse.actions;
      }
      
      throw new Error('解析结果不是数组格式');
    } catch (parseError) {
      logger.error('解析AI响应失败', { error: parseError });
      return [];
    }
    
  } catch (error: any) {
    logger.error(`生成测试动作失败: ${error.message}`);
    return [];
  }
}